package com.lhkj.ct.framework.web.exception;

import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.google.common.collect.Sets;
import com.lhkj.ct.base.enums.ReturnStatus;
import com.lhkj.ct.base.exception.BusinessException;
import com.lhkj.ct.base.exception.SystemException;
import com.lhkj.ct.base.exception.ValidatedException;
import com.lhkj.ct.base.exception.base.BaseException;
import com.lhkj.ct.base.model.ro.ActionResult;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.ExpiredCredentialsException;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 全局异常处理
 * </p>
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    private final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(Exception.class)
    public ActionResult<Void> defaultExceptionHandler(Exception e) {
        logger.error("Exception异常", e);
        return buildStackTraceResult(ReturnStatus.SC_INTERNAL_SERVER_ERROR.getCode(), e);
    }
    @ExceptionHandler(BaseException.class)
    public ActionResult<Void> defaultBaseExceptionHandler(BaseException e) {
        logger.error("BaseException异常", e);
        return buildResult(ReturnStatus.SC_INTERNAL_SERVER_ERROR.getCode(), e.getMessage());
    }

    @ExceptionHandler(SystemException.class)
    public ActionResult<Void> defaultServiceExceptionHandler(SystemException e) {
        logger.error("ServiceException异常", e);
        return buildErrorMessageResult(ReturnStatus.SC_INTERNAL_SERVER_ERROR.getCode(), e);
    }

    @ExceptionHandler(ValidatedException.class)
    public ActionResult<Void> defaultValidatedExceptionHandler(ValidatedException e) {
        logger.error("Exception异常", e);
        return buildResult(ReturnStatus.LOGICAL_VALIDATE_FAILED.getCode(), e.getMessage());
    }

    /**
     * 如果在Controller层中已经声明过BindingResult来获取信息则不会再抛出BindException，
     * 如果不在具体的Controller获取信息，可以统一通过捕获BindException处理BindingResult中的信息；
     * hibernate 参数校验异常（get)
     */
    @ExceptionHandler({ConstraintViolationException.class})
    public ActionResult<Void> defaultConstraintViolationException(ConstraintViolationException e) {
        Set<String> errors = Sets.newHashSet();
        for (ConstraintViolation<?> constraintViolation : e.getConstraintViolations()) {
            errors.add(constraintViolation.getMessage());
        }
        logger.error("ConstraintViolationException异常：" + e.getMessage());
        return buildResult(ReturnStatus.SC_BAD_REQUEST.getCode(), StringUtils.defaultIfBlank(String.join("\r", errors), "参数验证失败"));
    }

    /**
     * 如果在Controller层中已经声明过BindingResult来获取信息则不会再抛出BindException，
     * 如果不在具体的Controller获取信息，可以统一通过捕获BindException处理BindingResult中的信息；
     * hibernate 参数校验异常（get)
     */
    @ExceptionHandler({BindException.class})
    public ActionResult<Void> defaultBindException(BindException e) {
        logger.error("BindException异常：" + e.getMessage());
        if (e.hasErrors()) {
            String errorMsg = e.getFieldErrors().stream().map(FieldError::getDefaultMessage).collect(Collectors.joining("\r"));
            return buildResult(ReturnStatus.SC_BAD_REQUEST.getCode(), errorMsg);
        }
        return buildResult(ReturnStatus.SC_BAD_REQUEST.getCode(), e.getMessage());
    }

    /**
     * hibernate 参数校验异常(post)
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ActionResult<Void> defaultMethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
        logger.error("MethodArgumentNotValidException异常", e);
        Set<String> errors = Sets.newHashSet();
        for (FieldError error : e.getBindingResult().getFieldErrors()) {
            errors.add(error.getDefaultMessage());
        }
        return buildResult(ReturnStatus.SC_BAD_REQUEST.getCode(), String.join("\r", errors));
    }

    /**
     * get请求 @RequestParam校验 require = true
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public ActionResult<Void> defaultMissingServletRequestParameterException(MissingServletRequestParameterException e) {
        logger.error("MissingServletRequestParameterException异常：" + ExceptionUtils.getStackTrace(e));
        return buildErrorMessageResult(ReturnStatus.SC_BAD_REQUEST.getCode(), e);
    }

    /**
     * 处理 Http消息不可读异常，如GET请求不能使用@RequestBody
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ActionResult<Void> defaultHttpMessageNotReadableException(HttpMessageNotReadableException e) {
        logger.error("HttpMessageNotReadableException异常：", e);
        return buildErrorMessageResult(ReturnStatus.SC_BAD_REQUEST.getCode(), e);
    }

    /**
     * 404 找不到对应资源
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    public ActionResult<Void> noHandlerFoundException(NoHandlerFoundException e) {
        logger.error("NoHandlerFoundException异常：" + ExceptionUtils.getStackTrace(e));
        return buildResult(ReturnStatus.SC_NOT_FOUND.getCode(), "找不到对应资源");
    }

    /**
     * MybatisPlus执行异常
     */
    @ExceptionHandler(MybatisPlusException.class)
    public ActionResult<Void> defaultServiceExceptionHandler(MybatisPlusException e) {
        logger.error("MybatisPlusException异常", e);
        return buildErrorMessageResult(ReturnStatus.JDBC_ERROR.getCode(), e);
    }

//    /**
//     * MyBatisSystem执行异常
//     */
//    @ExceptionHandler(MyBatisSystemException.class)
//    public ActionResult<Void> defaultMyBatisSystemExceptionHandler(MyBatisSystemException e) {
//        logger.error("MyBatisSystemException异常", e);
//        return buildStackTraceResult(ReturnStatus.JDBC_ERROR.getCode(), e);
//    }

    /**
     * shiro 权限异常
     */
    @ExceptionHandler(AuthorizationException.class)
    public ActionResult<Void> defaultAuthenticationExceptionHandler(AuthenticationException e) {
        logger.error("org.apache.shiro.authc.AuthenticationException异常", e);
        return buildResult(ReturnStatus.SC_UNAUTHORIZED.getCode(), "抱歉，您无权限访问此页面或接口");
    }

    /**
     * 登录异常
     * @param e
     * @return
     */
    @ExceptionHandler(AuthenticationException.class)
    public ActionResult<Void> defaultAuthenticationExceptionHandler1(AuthenticationException e) {
        logger.error("org.apache.shiro.authc.AuthenticationException异常", e);
        return buildResult(ReturnStatus.SC_BAD_REQUEST.getCode(), e.getMessage());
    }

    /**
     * shiro 当尚未完成成功认证时，尝试执行授权操作时引发异常
     */
    @ExceptionHandler(UnauthenticatedException.class)
    public ActionResult<Void> defaultUnauthenticatedExceptionHandler(UnauthenticatedException e) {
        logger.error("UnauthenticatedException异常", e);
        return buildResult(ReturnStatus.NO_LOGIN.getCode(), e.getMessage());
    }

    /**
     * shiro 抛出以指示请求的操作或对请求的资源的访问是不允许的
     */
    @ExceptionHandler(UnauthorizedException.class)
    public ActionResult<Void> defaultUnauthorizedExceptionHandler(UnauthorizedException e) {
        logger.error("UnauthorizedException异常", e);
        return buildResult(ReturnStatus.SC_UNAUTHORIZED.getCode(), e.getMessage());
    }

    /**
     * shiro 凭证过期异常
     */
    @ExceptionHandler(ExpiredCredentialsException.class)
    public ActionResult<Void> defaultExpiredCredentialsExceptionHandler(ExpiredCredentialsException  e) {
        logger.error("ExpiredCredentialsException 异常", e);
        return buildResult(ReturnStatus.LOGIN_EXPIRE.getCode(), "登录已失效，请重新登录");
    }

    @ExceptionHandler(BusinessException.class)
    public ActionResult<Void> defaultBusinessExceptionHandler(BusinessException e) {
        logger.error("BusinessException异常", e);
        return buildResult(e.getCode(), e.getMessage());
    }

    @ExceptionHandler(IllegalArgumentException.class)
    public ActionResult<Void> defaultIllegalArgumentExceptionHandler(IllegalArgumentException e) {
        logger.error("IllegalArgumentException异常", e);
        return buildStackTraceResult(ReturnStatus.SC_BAD_REQUEST.getCode(), e);
    }

    /**
     * 构建异常返回值，抓取堆栈异常
     */
    private ActionResult<Void> buildStackTraceResult(int errorCode, Exception e) {
        return buildResult(errorCode, ExceptionUtils.getStackTrace(e));
    }

    /**
     * 构建异常返回值，抓取堆栈异常
     */
    private ActionResult<Void> buildErrorMessageResult(int errorCode, Exception e) {
        return buildResult(errorCode, e.getMessage());
    }

    /**
     * 构建异常返回值,自定义异常信息
     */
    private ActionResult<Void> buildResult(int errorCode, String msg) {
        return ActionResult.fail(errorCode, msg);
    }

}
